/***************************************************************************
 *   Copyright (C) 2004 by Nilesh Bansal                                   *
 *   nilesh@iitb.ac.in                                                     *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <QProcess>
#include <QTimer>
#include <QRegExp>

#include <klocale.h>
#include "ftpmonitorloop.h"
#include <kdebug.h>

FTPMonitorLoop::FTPMonitorLoop(FtpServerType ftpServer,QString whoPath,bool useSudo)
:QObject(),mFtpDaemon(ftpServer)
{
	mTimer = new QTimer(this);
	connect( mTimer, SIGNAL(timeout()), this, SLOT(execWho()) );
	mProc = new QProcess(this);
	mProcGrep = new QProcess(this);
	connect( mProc, SIGNAL(readyReadStandardOutput()),this, SLOT(readFromStdout()) );
	//we need grep for sftpd, so we need to redirect output from ps -ef to grep
	mProcGrep->setStandardOutputProcess(mProc);
	
	createExecCommand(ftpServer,useSudo,whoPath);
}


FTPMonitorLoop::~FTPMonitorLoop()
{
	delete mTimer;
	delete mProc;
	delete mProcGrep;
}

void FTPMonitorLoop::createExecCommand(FtpServerType ftpServer,bool useSudo,QString whoPath)
{
	QStringList arguments;
	QString execCommand;
			
	switch (ftpServer){
		case pureftpd:
			if (whoPath=="")
				execCommand += "/usr/sbin/pure-ftpwho";
			else
				execCommand += whoPath + "ftpwho";
			arguments  << "-s";
		  	break;
		case proftpd:
			if (whoPath=="")
				execCommand += "/usr/sbin/ftpwho";
			else
				execCommand += whoPath + "ftpwho";
			arguments << "-v" << "-o" << "oneline";
			break;
		case vsftpd:
			if (whoPath=="")
				execCommand += "/bin/ps";
			else
				execCommand += whoPath + "ps";
			arguments << "-fe";
			break;
		case ncftpd:
			break;
	}
	
	//if we use sudo, then ftpwho binary is first argumen of sudo command
	if (useSudo==true)
	{
	      arguments.prepend(execCommand);
	      execCommand="sudo";
	}
	
	mFtpWhoCommand.bin=execCommand;
	mFtpWhoCommand.args=arguments;
}

void FTPMonitorLoop::start(int interval,bool notifyNewClient)
{
	mAllowPopup=notifyNewClient;
	mTimer->start(interval*1000);
}

void FTPMonitorLoop::stop()
{
	mTimer->stop();
}

void FTPMonitorLoop::execWho()
{
	//kDebug(1234) << mFtpWhoCommand.bin << "+" << mFtpWhoCommand.args;
	if (mFtpDaemon == vsftpd)
	{
		mProcGrep->start(mFtpWhoCommand.bin,mFtpWhoCommand.args);
		mProc->start("grep",QStringList("vsftpd"));
	}
	else
		mProc->start(mFtpWhoCommand.bin,mFtpWhoCommand.args);
}

void FTPMonitorLoop::readFromStdout()
{
	if(mFtpDaemon == pureftpd)
		readFromStdoutPureFtpd();
	else if(mFtpDaemon == ncftpd)
		readFromStdoutNcFtpd();
	else if(mFtpDaemon == vsftpd)
		readFromStdoutVsFtpd();
	else if(mFtpDaemon == proftpd)
		readFromStdoutProFtpd();
}

// <client pid="17474" account="ftp" time="27" state=" DL " file="/uploads/archlinux-2009.02-core-i686.iso" host="sarka.xxxx.xx" localhost="minime.xxxx.cz" localport="21" />
// <client pid="17435" account="ftp" time="92" state="IDLE" file="" host="sarka.xxxx.xx" localhost="minime.xxxx.xx" localport="21" />
void FTPMonitorLoop::readFromStdoutPureFtpd()
{
	QVector<QStringList> listOfClients;
	QStringList clientRow;
	QStringList split;
	
	QStringList raw_string_list = QString(mProc->readAllStandardOutput()).split("\n");
	QStringList::Iterator it = raw_string_list.begin();
	
	for ( ; it != raw_string_list.end() ; it++ ) {
			split = it->split("|");
			if (split.count()!=12) continue;
			//"PID" << "Login" << "State" << "file" < "Host");
			//pid|acct|time|state|file|peer|local|port|current|total|%|bandwidth
			clientRow.clear();
			clientRow << split.at(0) << split.at(1) << split.at(3) /*+ " " + split.at(10) + "%"*/ << split.at(4) << split.at(5);
			listOfClients.push_back(clientRow);
	}
	
	checkClientRows(listOfClients);
}

//12391 ftp      [ 1m02s]  0m10s idle client: _none_ [::ffff:127.0.0.1] server: 127.0.0.2:21 (ProFTPD Anonymous Server) location: /
//12391 ftp      [ 3m10s] (  0%) RETR /bigfile.test KB/s: 40700.00 client: _none_ [::ffff:127.0.0.1] server: 127.0.0.2:21 (ProFTPD Anonymous Server) location: /
void FTPMonitorLoop::readFromStdoutProFtpd()
{
	QRegExp rx;
	QVector<QStringList> listOfClients;
	QStringList clientRow;
	
	QStringList raw_string_list = QString(mProc->readAllStandardOutput()).split("\n");
	
	QStringList::Iterator it = raw_string_list.begin();
	if(it == raw_string_list.end())
		return;
	
	for ( it++ ; it != raw_string_list.end() ; it++ ) {
		rx.setPattern("^(\\d+) (\\S+) .*s........ (.*) client:(.*) server:(.*) location:(.*)$");
		if (rx.indexIn(it->trimmed()) != -1) {
			clientRow.clear();
			clientRow << rx.cap(1) << rx.cap(2) << rx.cap(3) << rx.cap(4) << rx.cap(6);
			listOfClients.push_back(clientRow);
		}
	}
	
	checkClientRows(listOfClients);
}

//ftp      18677 18675  0 09:30 ?        00:00:00 vsftpd: 10.20.1.244/ftp: RETR /uploads/archlinux-2009.02-core-i686.iso
//ftp      18699 18697  0 09:30 ?        00:00:00 vsftpd: 10.20.1.244/ftp: IDLE 
void FTPMonitorLoop::readFromStdoutVsFtpd()
{
	QRegExp rx;
	QVector<QStringList> listOfClients;
	QStringList clientRow;
	
	QStringList processList = QString(mProc->readAllStandardOutput()).split("\n");
	
	QStringList::Iterator it = processList.begin();
	if(it == processList.end())
		return;
	
	for ( ; it != processList.end(); ++it )
	{
		if(it->contains("vsftpd") )
		{
			rx.setPattern("^(\\S+).*(\\d+).*vsftpd: (.*)/.*:(.*) (.*)$");
			if (rx.indexIn(it->trimmed()) != -1)
			{
				clientRow.clear();
				clientRow << rx.cap(2) << rx.cap(1)  << rx.cap(5) << rx.cap(4) << rx.cap(3);
				listOfClients.push_back(clientRow);
			}
		}
	}
	
	checkClientRows(listOfClients);
}

void FTPMonitorLoop::readFromStdoutNcFtpd()
{
//     delete ftp_string;
//     ftp_string=new QString("");
//     QString * raw_ftp_string = new QString(proc->readStdout ());
// 
//     ftp_string = raw_ftp_string;
//     no_of_connections =  (*ftp_string).contains("<tr>");
//     if(old_no_of_connections!=no_of_connections){
//         emit(change_in_conn()); 	
//         old_no_of_connections=no_of_connections;
//         emit(reverse());
//     }
// 
}


/// /////////////////// checks & emits to view /////////////////////////////////////////

void FTPMonitorLoop::checkClientRows(QVector<QStringList> newListOfClients)
{
        bool pairFound;
	
	//if count of connected clients has changed do something with icon
	if ( newListOfClients.count() != mListOfClients.count() )
		emit sigClientsCountChanged(newListOfClients.count());
	
	for (int i=0; i < mListOfClients.size(); ++i) {
		pairFound=false;
		for (int j=0; j < newListOfClients.size(); ++j) {
			if (mListOfClients.at(i).at(0) == newListOfClients.at(j).at(0)) {
				if (checkChangeForClient(mListOfClients.at(i),newListOfClients.at(j))==true)
				{
					mListOfClients.replace(i,newListOfClients.at(j));
					emit sigChangeRow(newListOfClients.at(j),i);
				}
				newListOfClients.remove(j);
				if (newListOfClients.size()>0) j--;
				pairFound=true;
				break;
			}
		}
		if (pairFound==false) {
			mListOfClients.remove(i);
			emit sigDelRow(i);
			if (mListOfClients.size()>0) i--;
		}
	}
	
	if (newListOfClients.size() > 0 )
	{
		for (int j=0; j < newListOfClients.size(); ++j) {
			mListOfClients.push_back(newListOfClients.at(j));
			emit sigAddRow(newListOfClients.at(j));
		}
		//call sigNewClientsConnected to show popup window if allowed in config
		if (mAllowPopup)
		  emit sigNewClientsConnected(newListOfClients.size());
	}
}

bool FTPMonitorLoop::checkChangeForClient(QStringList newClientRow,QStringList oldClientRow)
{
	switch(mFtpDaemon)
	{
		case proftpd:
			if (newClientRow.at(2) != oldClientRow.at(2) || newClientRow.at(4) != oldClientRow.at(4))
				return true;
			break;
		case pureftpd:
			//check also login, it's undefined (?) until user enter name
			if (newClientRow.at(1) != oldClientRow.at(1) || newClientRow.at(2) != oldClientRow.at(2) || 
			    newClientRow.at(3) != oldClientRow.at(3) )
				return true;
			break;
		case vsftpd:
			if (newClientRow.at(2) != oldClientRow.at(2) || newClientRow.at(3) != oldClientRow.at(3))
				return true;
			break;
		case ncftpd:
			//comes soon
			break;
	}
	return false;
}
